#!/bin/bash

########################################################################
#
# backup script to backup clients via rsync
#
# Copyright (C) 2008 Peter Kruse <pk@q-leap.de>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
#    USA
#
########################################################################

############################################################################
#
# run this script on the backup server,
# where you store the data from remote computers
#
############################################################################

############################################################################
#
# daily, weekly and monthly backups should use the same primary
# directory as the source for the hard links, directory structure:
# /backup_dir/latest
#             daily/0
#                   1
#                   .
#                   .
#                   .
#             weekly/0
#                    .
#                    .
#                    .
#             monthly/0
#                     .
#                     .
#                     .
#
############################################################################

############################################################################
#
# define defaults
#
############################################################################

destination=""
source_directory=""
rsync_opts="-a -q --delete"
first_directory=0
primary_directory=""
cycles=1
quiet=false
debug=false
rsync_cmd="rsync"
config_file=""

SCRIPTNAME="${0##*/}"
USAGE="usage: ${SCRIPTNAME} [-h]
    -b                  debug                    (${debug})
    -c <config file>    configuration file       (${config_file})
    -d <destination>    where to backup to       (${destination})
    -n <cycles>         keep this many cycles    (${cycles})
    -o                  print default values
    -p <primary dir>    primary backup directory (${primary_directory})
    -q                  quiet                    (${quiet})
    -r <rsync options>  rsync options            (${rsync_opts})
    -s <source>         host:directory to backup (${source_directory})
"

############################################################################
#
# functions
#
############################################################################

function execute_or_echo() {
    local retval
    if [ "${quiet}" = false ]; then
	echo "${@}"
    fi
    if [ "${debug}" = false ]; then
	eval "${@}"
    fi
    retval=$?
    return $retval
}

function print_default_values() {
    cat <<EOF
# -*- sh -*-
destination="${destination}"
source_directory="${source_directory}"
rsync_opts="${rsync_opts}"
first_directory="${first_directory}"
primary_directory="${primary_directory}"
cycles="${cycles}"
rsync_cmd="${rsync_cmd}"
quiet="${quiet}"
debug="${debug}"
EOF
}

############################################################################
#
# command line
#
############################################################################

while getopts :bc:d:hn:op:qr:s: OPT; do
    case $OPT in
	b)
	    debug=true
	    ;;
	c)
	    config_file="${OPTARG}"
	    ;;
	d)
	    destination="${OPTARG}"
	    ;;
	h)
	    echo "${USAGE}"
	    exit
	    ;;
	n)
	    cycles="${OPTARG}"
	    ;;
	o)
	    print_default_values
	    exit 0
	    ;;
	p)
	    primary_directory="${OPTARG}"
	    ;;
	q)
	    quiet=true
	    ;;
	r)
	    rsync_opts="${OPTARG}"
	    ;;
	s)
	    source_directory="${OPTARG}"
	    ;;
	*)
	    echo "${USAGE}" >&2
	    exit 2
    esac
done
shift $[ OPTIND - 1 ]

if [ $# -ne 0 ]; then
    echo "${SCRIPTNAME}: no arguments expected" >&2
    exit 2
fi


############################################################################
#
# sanity check
#
############################################################################

# if config file given source it first
if [ -n "${config_file}" ]; then
    if [ -f "${config_file}" ]; then
	. "${config_file}"
    else
	echo "configuration file \"${config_file}\" does not exit" >&2
	exit 1
    fi
fi

if [ -z "${source_directory}" ]; then
    echo "source directoy is empty, nothing to backup" >&2
    exit 1
fi

if [ -z "${primary_directory}" ]; then
    echo "primary directoy is empty, nothing to backup" >&2
    exit 1
else
    if [ ! -d "${primary_directory}" ]; then
	execute_or_echo mkdir -p "${primary_directory}"
	retval=$?
	if [ $retval -ne 0 ]; then
	    exit $retval
	fi
    fi
fi

# source can be in the format of host:directory, but not destination

case "$destination" in
    *:*)
	echo "destination \"${destination}\" must be local" >&2
	exit 1
	;;
esac


############################################################################
#
# action
#
############################################################################

# we cannot ignore errors
set -e

# rsync the backups n-1 -> n, n-2 -> n-1, ..., 1 -> 2
for((j=$cycles; j>0; j--)); do
    old=$((j - 1))
    if [ -d "$destination/$old" ]; then
	execute_or_echo ${rsync_cmd} ${rsync_opts} \
	    --link-dest="${primary_directory}" \
	    "${destination}/${old}/" "${destination}/${j}/"
    fi
done

# rsync the primary directory to the first snapshot directory
if [ ! -d "${destination}" ]; then
    execute_or_echo mkdir -p "${destination}"
fi

execute_or_echo ${rsync_cmd} ${rsync_opts} \
    --link-dest="${primary_directory}" \
	"${primary_directory}/" \
	"${destination}/${first_directory}"

# we can ignore some rsync errors
set +e

# rsync onto the primary directory
execute_or_echo ${rsync_cmd} ${rsync_opts} \
    --link-dest="${primary_directory}" \
    ${source_directory} \
    "${primary_directory}"
retval=$?
case "$retval" in
    0|23|24)
	:
	;;
    *)
	exit $retval
esac
